package com.xcode.unit;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.*;
import com.github.javaparser.ast.body.*;
import com.github.javaparser.ast.expr.*;
import com.github.javaparser.ast.stmt.*;
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.ast.type.Type;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xcode.unit.builder.LUnitTestBuilder;
import com.xcode.unit.config.UnitTestConfig;
import com.xcode.unit.constant.ClassConstants;
import com.xcode.unit.constant.ExpressionEnum;
import com.xcode.unit.constant.UnitTestClassConstants;
import com.xcode.unit.model.*;
import com.xcode.unit.util.LFileUtil;
import com.xcode.unit.util.LReflectionUtils;
import com.xcode.unit.util.LStringUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

/**
 * -DjavaFile=UserServiceImpl.java
 * -DjavaPath=D:\workspace\study\xcode-unit-test\src\main\java\com\xcode\biz\service\impl
 * -DmethodName=getUser
 * -DpowerMock=true
 * -DjarFile=D:\tmp\
 *
 * @author liuziying
 * @Description 解析单元测试类
 * @Date 2020/12/22
 */
public class UnitTestApp {

    static Logger logger = LoggerFactory.getLogger(UnitTestApp.class);

    public static void main(String[] args) {
        unitTestConfig = loadConfig();
        if(ObjectUtils.isEmpty(unitTestConfig.getJavaFile())){
            Collection<File> javaFiles =  FileUtils.listFiles(new File(unitTestConfig.getJavaPath()), new String[]{"java"}, true);
            javaFiles.forEach(javaFile -> {
                javaSourceParse(javaFile);
                clearClassResource();
            });
        }else{
            javaSourceParse(new File(unitTestConfig.getJavaPath() + File.separator + unitTestConfig.getJavaFile()));
            clearClassResource();
        }
    }

    /**
     * 加载配置
     * @return
     */
    private static UnitTestConfig loadConfig(){
        UnitTestConfig unitTestConfig = new UnitTestConfig();
        String javaPath = System.getProperty("javaPath");
        String javaFile = System.getProperty("javaFile");
        String methodName = System.getProperty("methodName");
        String classPath = System.getProperty("classPath");
        String libPath = System.getProperty("libPath");
        String jarFile = System.getProperty("jarFile");
        String outDir = System.getProperty("outDir");
        boolean powerMock = Boolean.valueOf(System.getProperty("powerMock", "false"));
        //method or file
        String ignore = System.getProperty("ignore");
        Assert.notNull(javaPath, "请维护VM参数:-DjavaPath");
        Assert.notNull(jarFile, "请维护VM参数:-DjarFile");
        Assert.notNull(outDir, "请维护VM参数:-DoutDir");
        Assert.isTrue(jarFile.endsWith(".jar"), "请正确维护VM参数:-DjarFile（路径+文件名.jar）");

        unitTestConfig.setJavaPath(javaPath);
        unitTestConfig.setJavaFile(javaFile);
        unitTestConfig.setMethodName(methodName);
        unitTestConfig.setClassPath(classPath);
        unitTestConfig.setLibPath(libPath);
        unitTestConfig.setJarFile(jarFile);
        unitTestConfig.setOutDir(outDir);
        unitTestConfig.setPowerMock(powerMock);

        if(!ObjectUtils.isEmpty(unitTestConfig.getMethodName())){
            String[] split = unitTestConfig.getMethodName().split(UnitTestClassConstants.SEPARATOR_COMMA);
            selectMethods = Arrays.asList(split);
        }
        if(!ObjectUtils.isEmpty(ignore)){
            ignoreType = ignore.trim();
        }

        return unitTestConfig;
    }

    static UnitTestConfig unitTestConfig;
    static SourceClassModel sourceClassModel = new SourceClassModel();
    static List<String> selectMethods = Lists.newArrayList();
    static List<String> ignoreFileds = Lists.newArrayList("log","logger");
    static String ignoreType;

    /**
     * 方法体行信息
     */
    static List<Node> allBodyNodes = Lists.newArrayList();
    static Queue<String> methodBodyMockQueue = Lists.newLinkedList();
    static List<String> asserts = Lists.newArrayList();
    static List<String> verifies = Lists.newArrayList();
    /**
     * 方法内变量
     * for(Type variable) var-type
     */
    static Map<String,String> methodInnerVariable = Maps.newHashMap();

    /**
     * 类资源
     */
    private static void clearClassResource(){
        sourceClassModel = new SourceClassModel();
        methodBodyMockQueue = Lists.newLinkedList();
        allBodyNodes = Lists.newArrayList();
        asserts = Lists.newArrayList();
        verifies = Lists.newArrayList();
        methodInnerVariable = Maps.newHashMap();
    }

    /**
     * 方法资源
     */
    private static void clearMethodResource(){
        methodBodyMockQueue = Lists.newLinkedList();
        allBodyNodes = Lists.newArrayList();
        asserts = Lists.newArrayList();
        verifies = Lists.newArrayList();
        methodInnerVariable = Maps.newHashMap();
    }


    private static void javaSourceParse(File javaFile){
        CompilationUnit parse = new CompilationUnit();
        try {
            parse = StaticJavaParser.parse(javaFile);
        }catch (FileNotFoundException e){
            logger.warn("解析Java文件不存在:{}",javaFile.getName());
        }

        final NodeList<TypeDeclaration<?>> types = parse.getTypes();
        for (TypeDeclaration<?> type: types) {
            final NodeList<Modifier> modifiers = type.getModifiers();
            for(Modifier modifier : modifiers){
                String modifierStr = modifier.getKeyword().asString();
                if(StringUtils.endsWithIgnoreCase(modifierStr, ClassConstants.TYPE_MODIFIER_ABSTRACT)){
                    return;
                }
            }
        }

        sourceClassModel.setPackageName(parse.getPackageDeclaration().get().getNameAsString());
        sourceClassModel.setClassName(parse.getPrimaryTypeName().get());
        /*
        解析import
         */
        parseImport(parse);
        /*
        解析字段
         */
        parseField(parse);

        /*
        遍历所有方法列表
         */
        List<MethodDeclaration> methods = parse.findAll(MethodDeclaration.class);
        List<String> methodNames = Lists.newArrayList();
        for (MethodDeclaration method : methods){
            methodNames.add(method.getNameAsString());
        }
        sourceClassModel.setMethodNames(methodNames);
        Map<String, MethodModel> methodModels = Maps.newHashMap();
        for (MethodDeclaration method : methods){
            if(ObjectUtils.nullSafeEquals(ignoreType, "method")){
                if(selectMethods.contains(method.getNameAsString())){
                    continue;
                }
            }else{
                if(!CollectionUtils.isEmpty(selectMethods) && !selectMethods.contains(method.getNameAsString())){
                    continue;
                }
            }
            MethodModel methodModel = new MethodModel();
            methodModel.setMethodName(method.getNameAsString());
            logger.info("Begin: 构建方法：{} ", sourceClassModel.getPackageName() + "." +sourceClassModel.getClassName() +"#"+ methodModel.getMethodName());
            NodeList<Modifier> modifiers = method.getModifiers();
            List<String> tmpModifiers = Lists.newArrayList();
            modifiers.forEach(modifier -> {
                tmpModifiers.add(modifier.getKeyword().name());
            });
            methodModel.setModifiers(tmpModifiers);

            if(!unitTestConfig.isPowerMock()){
                if(tmpModifiers.contains("PRIVATE")){
                    continue;
                }
            }

            /*根据返回值类型-自动断言*/
            methodModel.setReturnType(method.getType());
            NodeList<ReferenceType> thrownExceptions = method.getThrownExceptions();
            List<String> tmpThrownExceptions = Lists.newArrayList();
            for(ReferenceType thrownException : thrownExceptions){
                tmpThrownExceptions.add(thrownException.getElementType().asString());
            }
            /*根据方法throws异常- 自动断言异常*/
            methodModel.setExceptionTypes(tmpThrownExceptions);

            //1.构建入参-参数类型-构造实例
            methodModel.setParameters(buildMethodParameter(method.getParameters()));

            Optional<BlockStmt> body = method.getBody();
            BlockStmt blockStmt = body.get();
            //方法体行信息
            allBodyNodes = blockStmt.getChildNodes();
            logger.info("方法体行数： {}", allBodyNodes.size());
            //2.遍历方法体-查找需要Mock的（入参、出参）-Mock方式
            baseStatements(methodModel, allBodyNodes);

            //3.invoke单测方法
            invokeUnitMethod(methodModel);

            //4. assert and verfy
            buildAssertAndVerfy(methodModel);

            methodModel.setBodyQueue(Lists.newLinkedList(methodBodyMockQueue));
            //同名方法输出
            if(methodModels.containsKey(methodModel.getMethodName())){
                methodModel.setMethodName(methodModel.getMethodName() + "_" + methodModel.getParameters().size() + "_");
            }
            methodModels.put(methodModel.getMethodName(),methodModel);

            while (methodBodyMockQueue.size() > 0){
                logger.info("body: {}", methodBodyMockQueue.poll());
            }
            logger.info("End: 构建方法：{} ", sourceClassModel.getPackageName() + "." +sourceClassModel.getClassName() +"#"+ methodModel.getMethodName());
            clearMethodResource();
        }
        sourceClassModel.setMethodModels(methodModels);
        //开始生成单测类
        LUnitTestBuilder.builder()
                .addSourceClassModel(sourceClassModel)
                .configUnitTest(unitTestConfig)
                .create();
    }

    /**
     * 解析Import
     * @param sourceJava
     */
    private static void parseImport(CompilationUnit sourceJava){
        List<ImportDeclaration> imports = sourceJava.findAll(ImportDeclaration.class);
        Map<String, String> importStatics = Maps.newHashMap();
        Map<String, String> importGenerals = Maps.newHashMap();
        for(ImportDeclaration importDeclaration : imports){
            String tmpKey = LStringUtils.getLastCharacterBySeparator(importDeclaration.getNameAsString(), UnitTestClassConstants.SEPARATOR_DOT);
            if(importDeclaration.isStatic()){
                if(importDeclaration.toString().contains("*")){
                    importStatics.put(tmpKey, importDeclaration.getNameAsString() + ".*");
                }else {
                    importStatics.put(tmpKey, importDeclaration.getNameAsString());
                }
            }else {
                if(importDeclaration.toString().contains("*")){
                    importGenerals.put(tmpKey, importDeclaration.getNameAsString() + ".*");
                }else {
                    importGenerals.put(tmpKey, importDeclaration.getNameAsString());
                }
            }
        }
        sourceClassModel.setImportStatics(importStatics);
        sourceClassModel.setImportGenerals(importGenerals);
    }

    /**
     * 解析Field
     * @param sourceJava
     */
    private static void parseField(CompilationUnit sourceJava){
        List<FieldDeclaration> fields = sourceJava.findAll(FieldDeclaration.class);
        List<FieldModel> tmpFields = Lists.newArrayList();
        Map<String, FieldModel> tmpConstantFields = Maps.newHashMap();
        for (FieldDeclaration field : fields){
            NodeList<AnnotationExpr> annotations = field.getAnnotations();
            if(annotations.isNonEmpty()){
                List<String> tmpAnnotations = Lists.newArrayList();
                for(AnnotationExpr annotation : annotations){
                    tmpAnnotations.add(annotation.getNameAsString());
                }

                NodeList<Modifier> modifiers = field.getModifiers();
                List<String> tmpModifiers = Lists.newArrayList();
                for (Modifier modifier : modifiers){
                    tmpModifiers.add(modifier.getKeyword().name());
                }

                NodeList<VariableDeclarator> variables = field.getVariables();
                for(VariableDeclarator variable : variables){
                    FieldModel fieldModel = new FieldModel();
                    fieldModel.setType(field.getElementType().asString());
                    fieldModel.setName(variable.getNameAsString());
                    fieldModel.setModifiers(tmpModifiers);
                    fieldModel.setAnnotations(tmpAnnotations);
                    tmpFields.add(fieldModel);
                }
            }else {
                NodeList<Modifier> modifiers = field.getModifiers();
                List<String> tmpModifiers = Lists.newArrayList();
                for (Modifier modifier : modifiers){
                    tmpModifiers.add(modifier.getKeyword().name());
                }

                NodeList<VariableDeclarator> variables = field.getVariables();
                for(VariableDeclarator variable : variables){
                    FieldModel fieldModel = new FieldModel();
                    fieldModel.setType(field.getElementType().asString());
                    fieldModel.setName(variable.getNameAsString());
                    fieldModel.setModifiers(tmpModifiers);
                    if(variable.getInitializer().isPresent()){
                        fieldModel.setValue(variable.getInitializer().get().toString());
                        tmpConstantFields.put(fieldModel.getName(), fieldModel);
                    }
                }
            }
        }
        sourceClassModel.setFieldModels(tmpFields);
        sourceClassModel.setInstantiateFieldModels(tmpConstantFields);
    }

    private static void buildAssertAndVerfy(MethodModel methodModel){
        if(!methodModel.getReturnType().isVoidType()){
            asserts.forEach(ar -> {
                addQueue(ar);
            });
        }
        verifies.forEach(vf -> {
            addQueue(vf);
        });
    }

    /**
     * 调用单测方法
     */
    private static void invokeUnitMethod(MethodModel methodModel){
        List<String> modifiers = methodModel.getModifiers();
        if(modifiers.contains("PRIVATE")){
            invokePrivateMethod(methodModel);
        }else {
            String className = LStringUtils.toLowerCaseFirst(sourceClassModel.getClassName());
            String methodName = methodModel.getMethodName();
            Map<String, ParameterModel> parametersMap = methodModel.getParameters();
            Set<String> parameterNames = parametersMap.keySet();
            String methodParameterList = StringUtils.join(parameterNames.toArray(), ",");
            Type returnType = methodModel.getReturnType();
            String invokeExpression =  className + "." + methodName + "(" + methodParameterList + ")";
            if(!returnType.isVoidType()){
                invokeExpression = returnType + " invokeResult = " +  invokeExpression;
                addAssert("invokeResult");
            }
            addQueue(invokeExpression);
        }
    }

    private static void invokePrivateMethod(MethodModel methodModel){
        String className = LStringUtils.toLowerCaseFirst(sourceClassModel.getClassName());
        String methodName = methodModel.getMethodName();
        Map<String, ParameterModel> parametersMap = methodModel.getParameters();
        String parameters = "";
        for(int i = 0; i < parametersMap.size(); i++){
            parameters +=  ",any()";
        }
        String invokeExpression = "Object invokeResult =  Whitebox.invokeMethod(" + className +",\""+ methodName +"\""+ parameters + ")";
        addQueue(invokeExpression);
    }

    /**
     * 2.遍历方法体-查找需要Mock的（入参、出参）-Mock方式
     * @param methodModel
     * @param nodes
     */
    private static void baseStatements(MethodModel methodModel, List<Node> nodes){
        for (Node node : nodes){
            preMockStmtJudge(node, methodModel);
        }
    }

    /**
     * 判断表达式，选择不同的处理方案
     * @param node
     * @param methodModel
     */
    private static void preMockStmtJudge(Node node, MethodModel methodModel){
        //正常表达式
        if(node instanceof ExpressionStmt){
            Expression expression = ((ExpressionStmt) node).getExpression();
            if(expression instanceof MethodCallExpr){
                boolean hasLambda = doMockMethodCallExpr((MethodCallExpr) expression, methodModel);
                if(hasLambda){
                    return;
                }
            }
            if(expression instanceof ObjectCreationExpr){
                boolean hasLambda = doMockObjectCreationExpr((ObjectCreationExpr)expression, methodModel);
                if(hasLambda){
                    return;
                }
            }
            doMockExpressionStmt((ExpressionStmt)node, methodModel);
        }
        //条件表达式块
        if(node instanceof IfStmt){
            doMockIfExpressionStmt((IfStmt)node, methodModel);
        }
        if(node instanceof SwitchStmt){
            doMockSwitchExpressionStmt((SwitchStmt)node, methodModel);
        }
        //foreach循环 for循环
        if(node instanceof ForStmt || node instanceof ForEachStmt){
            doMockForExpressionStmt(node, methodModel);
        }
        //Try-Catch表达式块
        if(node instanceof TryStmt){
            doMockTryExpressionStmt((TryStmt)node, methodModel);
        }
        if(node instanceof ThrowStmt){
            Expression expressionThrow = ((ThrowStmt) node).getExpression();
            addQueue(buildPartBegin( "ThrowStmt:" + node.getBegin().get().line));
            if(expressionThrow instanceof ObjectCreationExpr){
                addQueue(thrownExpect(((ThrowStmt) node).getExpression().asObjectCreationExpr().getType().asString()));
            }
            if(expressionThrow instanceof NameExpr){
                //TODO 优化 精准异常类型
                addQueue(thrownExpect("Exception"));
            }
            addQueue(buildPartEnd( "ThrowStmt:" + node.getBegin().get().line));
        }
        //Return表达式块
        if(node instanceof ReturnStmt){
            doMockReturnExpressionStmt((ReturnStmt)node, methodModel);
        }
    }

    /**
     * 表达式中包括忽略的字段
     *
     * log / logger
     *
     * @param expressionStr
     * @return
     */
    private static boolean ignoreExpression(String expressionStr){
        if(ObjectUtils.isEmpty(expressionStr)){
            return false;
        }
        for(String ignoreFiled : ignoreFileds){
            if(expressionStr.contains(ignoreFiled+".")){
                logger.info("忽略表达式：{}", expressionStr);
                return true;
            }
        }
        return false;
    }

    /**
     * Lambda Thread new T(()->{});
     * @param objectCreationExpr
     * @param methodModel
     */
    private static boolean doMockObjectCreationExpr(ObjectCreationExpr objectCreationExpr, MethodModel methodModel){
        boolean hasLambdaExpr = false;
        List<Node> childNodes = objectCreationExpr.getChildNodes();
        for(Node childNode : childNodes){
            if(childNode instanceof LambdaExpr){
                List<Node> expressionNodes = ((LambdaExpr) childNode).getBody().getChildNodes();
                baseStatements(methodModel, expressionNodes);
                hasLambdaExpr = true;
            }
        }
        return hasLambdaExpr;
    }

    /**
     * Lambda foreach
     * @param methodCallExpr
     * @param methodModel
     */
    private static boolean doMockMethodCallExpr(MethodCallExpr methodCallExpr, MethodModel methodModel){
        boolean hasLambdaExpr = false;
        List<Node> childNodes = methodCallExpr.getChildNodes();
        for(Node childNode : childNodes){
            if(childNode instanceof LambdaExpr){
                List<Node> expressionNodes = ((LambdaExpr) childNode).getBody().getChildNodes();
                baseStatements(methodModel, expressionNodes);
                hasLambdaExpr = true;
            }
        }
        return hasLambdaExpr;
    }

    /**
     * Mock If表达式块
     * @param returnStmt
     * @param methodModel
     */
    private static void doMockReturnExpressionStmt(ReturnStmt returnStmt, MethodModel methodModel){
        if(!returnStmt.getExpression().isPresent()){
            return;
        }
        Expression expression = returnStmt.getExpression().get();
        if(expression instanceof ConditionalExpr){
            //三目运算符
            if(expression.toString().contains("?") && expression.toString().contains(":")){
                String[] splitExpressions = expression.toString().split(UnitTestClassConstants.SEPARATOR_QUESTION_MARK);
                String[] split = splitExpressions[1].split(":");
                doMockMethodCallExpressionStmt(split[0], methodModel);
                doMockMethodCallExpressionStmt(split[1], methodModel);
            }
        }
        if(expression instanceof MethodCallExpr){
            doMockMethodCallExpressionStmt(expression.toString(), methodModel);
        }

        if(expression instanceof NameExpr){
            logger.warn("return NameExpr表达式：{}", expression);
        }

    }

    /**
     * Mock Try表达式块
     * @param tryStmt
     * @param methodModel
     */
    private static void doMockTryExpressionStmt(TryStmt tryStmt, MethodModel methodModel){
        List<Node> childNodes = tryStmt.getChildNodes();
        for(Node partStmt : childNodes){
            if(partStmt instanceof BlockStmt){
                baseStatements(methodModel, partStmt.getChildNodes());
            }
            if(partStmt instanceof CatchClause){
                baseStatements(methodModel, ((CatchClause) partStmt).getBody().getChildNodes());
            }
        }
    }

    /**
     * Mock For表达式块
     * @param node
     * @param methodModel
     */
    private static void doMockForExpressionStmt(Node node, MethodModel methodModel){
        if(node instanceof ForStmt){
            baseStatements(methodModel, ((ForStmt) node).getBody().asBlockStmt().getChildNodes());
        }
        if(node instanceof ForEachStmt){
            baseStatements(methodModel, ((ForEachStmt) node).getBody().asBlockStmt().getChildNodes());
        }
    }

    /**
     * 添加方法内变量-是否入body队列
     * @param varName
     * @param isqueue
     */
    private static void addMethodInnerVariable(String varName, boolean isqueue){
        varName = varName.trim();
        if(!methodInnerVariable.containsKey(varName)){
            //创建复合对象
            String variableType = getTypeByVariableName(varName, allBodyNodes);
            if(ObjectUtils.isEmpty(variableType)){
                logger.warn("变量：{} 对应的类型为空，请核实!!!", varName);
                return;
            }
            if(isqueue){
                addQueue(autoCreateObject(variableType, varName));
            }
            //for变量放入集合中
            methodInnerVariable.put(varName, variableType);
        }
    }

    /**
     * Mock switch表达式块
     * @param switchStmt
     * @param methodModel
     */
    private static void doMockSwitchExpressionStmt(SwitchStmt switchStmt, MethodModel methodModel){
        List<Node> childNodes = switchStmt.getChildNodes();
        for(Node partStmt : childNodes){
            if(partStmt instanceof SwitchEntry){
                addQueue(buildPartBegin( "SwitchStmt:" + partStmt.getBegin().get().line));
                baseStatements(methodModel, partStmt.getChildNodes());
                addQueue(buildPartEnd("SwitchStmt:" + partStmt.getBegin().get().line));
            }
        }
    }

    /**
     * Mock If表达式块
     * @param ifStmt
     * @param methodModel
     */
    private static void doMockIfExpressionStmt(IfStmt ifStmt, MethodModel methodModel){
        List<Node> childNodes = ifStmt.getChildNodes();
        for(Node partStmt : childNodes){
            if(partStmt instanceof BlockStmt){
                addQueue(buildPartBegin( "IfStmt:" + partStmt.getBegin().get().line));
                baseStatements(methodModel, partStmt.getChildNodes());
                addQueue(buildPartEnd("IfStmt:"+partStmt.getBegin().get().line));
            }
            if(partStmt instanceof IfStmt){
                addQueue(buildPartBegin("IfStmt:" + partStmt.getBegin().get().line));
                doMockIfExpressionStmt((IfStmt)partStmt, methodModel);
                addQueue(buildPartEnd("IfStmt:"+partStmt.getBegin().get().line));
            }
        }
    }

    /**
     * Mock正常表达式块
     * @param expressionStmt
     * @param methodModel
     */
    private static void doMockExpressionStmt(ExpressionStmt expressionStmt, MethodModel methodModel){
        Expression expression = expressionStmt.getExpression();
        if(isMock(expression.toString(), methodModel) > 0){
            MockExpressionModel mockExpressionModel = toMockExpressionModel(expression.toString(), methodModel);
            instanceParameterAndReturn(mockExpressionModel, methodModel);
            /*
            3. Mock表达式
                根据表达式返回值类型， 判断使用Mock方式
             */
            if(ObjectUtils.nullSafeEquals(ExpressionEnum.EQUALITY, mockExpressionModel.getType())){
                //mockExpressionModel.setVariableName(getReturnVariableName(mockExpressionModel.getVariableName()));
                addQueue(buildMockitoWhenHasReturn(mockExpressionModel));
            }else{
                addQueue(buildMockitoWhenNotReturn(mockExpressionModel));
            }
        }
    }

    /**
     * Mock return处理
     * @param expressionStmtStr
     * @param methodModel
     */
    private static void doMockMethodCallExpressionStmt(String expressionStmtStr, MethodModel methodModel){
        if(isMock(expressionStmtStr, methodModel) > 0){
            MockExpressionModel mockExpressionModel = toMockExpressionModel(expressionStmtStr, methodModel);
            mockExpressionModel.setReturnType(methodModel.getReturnType().asString());
            String varTmp = methodModel.getReturnType().asString().replace("<", "").replace(">", "").replace(",","").replace(" ","");
            mockExpressionModel.setVariableName(LStringUtils.toLowerCaseFirst(varTmp) + "MethodCall");
            instanceParameterAndReturn(mockExpressionModel, methodModel);
            /*
            3. Mock表达式
                根据表达式返回值类型， 判断使用Mock方式
             */
            addQueue(autoCreateObject(mockExpressionModel.getReturnType(), mockExpressionModel.getVariableName()));
            addQueue(buildMockitoWhenHasReturn(mockExpressionModel));
        }
    }

    /**
     * 判断该变量是否属于 方法参数或 方法变量
     * @param var
     * @param methodModel
     * @return
     */
    private static boolean isMethodParameterOrVariable(String var, MethodModel methodModel){
        if(methodModel.exist(var) || methodInnerVariable.containsKey(var)){
            return true;
        }
        return false;
    }

    /**
     * 实例化入参和返回值
     * @param mockExpressionModel
     * @param methodModel
     */
    private static void instanceParameterAndReturn(MockExpressionModel mockExpressionModel, MethodModel methodModel){
        /*, MethodModel methodModel
        1. 表达式入参
            判断表达式入参是否为方法入参，若是：不需要声明，若不是：需要声明
         */
        List<String> parameterNames = mockExpressionModel.getParameterNames();
        for(String parameterName : parameterNames){
            //若是封装变量.属性-分解获取封装变量
            parameterName = parameterName.contains(".") ? parameterName.substring(0, parameterName.indexOf(".")) : parameterName;
            if(!methodModel.exist(parameterName)){
                addMethodInnerVariable(parameterName, true);
            }
        }

        //2. 表达式返回值
        if(ObjectUtils.nullSafeEquals(ExpressionEnum.EQUALITY, mockExpressionModel.getType())){
            addMethodInnerVariable(mockExpressionModel.getVariableName(),false);
            //getReturnVariableName(mockExpressionModel.getVariableName()))
            addQueue(autoCreateObject(mockExpressionModel.getReturnType(), mockExpressionModel.getVariableName()));
        }
    }

    private static String getReturnVariableName(String variableName){
        return ObjectUtils.isEmpty(variableName) ? "" : variableName.trim() + "Return";
    }

    /**
     * 入队列
     * @param body
     */
    private static void addQueue(String body){
        if(methodBodyMockQueue.contains(body)){
            return;
        }
        methodBodyMockQueue.add(body);
    }

    /**
     * 有返回值的Mock
     * @param mockExpressionModel
     * @return
     */
    private static String buildMockitoWhenHasReturn(MockExpressionModel mockExpressionModel){
        if(unitTestConfig.isPowerMock()){
            return "PowerMockito.when(" +
                    mockExpressionModel.getExpression().trim() +
                    ").thenReturn(" +
                    mockExpressionModel.getVariableName() +
                    ")";
        }
        return "when(" +
                mockExpressionModel.getExpression().trim() +
                ").thenReturn(" +
                mockExpressionModel.getVariableName() +
                ")";
    }

    /**
     * 有返回值的Mock
     * @param mockExpressionModel
     * @return
     */
    private static String buildMockitoWhenNotReturn(MockExpressionModel mockExpressionModel){
        String expression = mockExpressionModel.getExpression();
        String var = expression.substring(0, expression.indexOf(".")).trim();
        String methodExpr = expression.substring(expression.indexOf(".") + 1);
        return "doNothing().when(" +
                var +
                ")." +
                methodExpr;
    }

    private static String buildPartBegin(String lable){
        return  "//" + lable + ":Begin";
    }

    private static String buildPartEnd(String lable){
        return "//" + lable + ":End";
    }

    /**
     * 表达式语句转 Mock表达式模型
     * eg： AddressDTO addressByUserCode = addressService.getAddressByUserCode(userCode);
     *      userService.saveUserCode(userCode);
     *      UserDTO userDTO = getUser(userCode);
     *      getUser(userCode);
     * @param expressionStmt
     * @return
     */
    private static MockExpressionModel toMockExpressionModel(String expressionStmt, MethodModel methodModel){
        MockExpressionModel mockExpressionModel = new MockExpressionModel();
        expressionStmt = expressionStmt.replaceAll(UnitTestClassConstants.SEPARATOR_SEMICOLON, "");
        if(expressionStmt.contains(UnitTestClassConstants.EQUALS)){
            mockExpressionModel.setType(ExpressionEnum.EQUALITY);
            String[] splitRightLeft = expressionStmt.split(UnitTestClassConstants.SEPARATOR_EQUALS);
            //有声明类型和变量
            if(splitRightLeft[0].split(UnitTestClassConstants.SEPARATOR_SPACE_ONE).length > 1){
                String[] splitRight = splitRightLeft[0].split(UnitTestClassConstants.SEPARATOR_SPACE_ONE);
                mockExpressionModel.setReturnType(splitRight[0]);
                mockExpressionModel.setVariableName(splitRight[1]);
            }else{
                //没有声明类型只有变量
                mockExpressionModel.setReturnType(getTypeByVariableName(splitRightLeft[0], allBodyNodes));
                mockExpressionModel.setVariableName(splitRightLeft[0]);
            }
            mockExpressionModel.setExpression(splitRightLeft[1]);
            //分割 获取表达式入参
            if(splitRightLeft[1].contains(UnitTestClassConstants.OPEN_PARENTHESIS)){
                //分割 获取表达式入参
                mockExpressionModel.setParameterNames(getExpressionParameterList(expressionStmt, methodModel));
            }
        }else{
            /**
             * eg: expressionStmt格式：A.B(userDTO);
             */
            //当没有明确返回值时，A.B格式时(除this.A) TODO  方法是否在当前类，没有则继续查找父类
            /*if(!expressionStmt.contains(ClassConstants.CLASS_AGENT_THIS+".")){
                String fieldName = expressionStmt.substring(0, expressionStmt.indexOf("."));
                String methodName = expressionStmt.substring(expressionStmt.indexOf(".") + 1, expressionStmt.indexOf("("));
                String[] count = expressionStmt.split(",");
                final String methodReturnType = LUnitTestBuilder.builder()
                        .addSourceClassModel(sourceClassModel)
                        .configUnitTest(unitTestConfig)
                        .getMethodReturnType(fieldName, methodName, count.length);
            }*/

            mockExpressionModel.setType(ExpressionEnum.RIGHT);
            mockExpressionModel.setExpression(expressionStmt);
            //分割 获取表达式入参
            mockExpressionModel.setParameterNames(getExpressionParameterList(expressionStmt, methodModel));
        }
        return mockExpressionModel;
    }

    /**
     * 根据变量获取类型
     * @param variableName
     * @param nodes
     * @return
     */
    private static String getTypeByVariableName(String variableName, List<Node> nodes){
        variableName = variableName.trim();
        for (Node node : nodes){
            if(node instanceof NameExpr){
                logger.warn("NameExpr表达式：{}", node);
                continue;
            }
            if(node instanceof LiteralExpr){
                logger.warn("Literal表达式：{}", node);
                continue;
            }
            if(node instanceof BreakStmt){
                logger.warn("BreakStmt表达式：{}", node);
                continue;
            }
            if(node instanceof BinaryExpr || node instanceof UnaryExpr){
                logger.warn("操作符表达式：{}", node);
                continue;
            }
            if(node instanceof ExpressionStmt){
                //eg: List<UserDTO> userDTOS = Lists.newArrayList();
                Expression expression = ((ExpressionStmt) node).getExpression();
                if(expression instanceof BinaryExpr || expression instanceof UnaryExpr){
                    logger.warn("操作符表达式：{}", expression);
                    continue;
                }
                if(expression instanceof MethodCallExpr){
                    //为了支持Lambda forEach
                    List<Node> childNodes = expression.getChildNodes();
                    for(Node childNode : childNodes ){
                        if(childNode instanceof NameExpr){
                            String nameAsString = ((NameExpr) childNode).getNameAsString();
                            if(methodInnerVariable.containsKey(nameAsString) && ObjectUtils.nullSafeEquals(nameAsString.trim(), variableName)){
                                //获取集合元素类型
                                String collectionType = methodInnerVariable.get(nameAsString);
                                return getCollectionElementType(collectionType);
                            }
                        }
                        if(childNode instanceof LambdaExpr){
                            List<Node> expressionNodes = ((LambdaExpr) childNode).getBody().getChildNodes();
                            return getTypeByVariableName(variableName, expressionNodes);
                        }
                    }
                    continue;
                }
                String minExpression = expression.toString().replaceAll(UnitTestClassConstants.SEPARATOR_SEMICOLON, "").trim();
                if(minExpression.contains(UnitTestClassConstants.EQUALS)){
                    String variableAndTypeTmp = "";
                    if(minExpression.contains(UnitTestClassConstants.EQUALS_DOUBLE)){
                        variableAndTypeTmp = minExpression.substring(0, minExpression.indexOf(UnitTestClassConstants.EQUALS)).trim();
                    }else{
                        String[] leftVariableAndType = minExpression.split(UnitTestClassConstants.SEPARATOR_EQUALS);
                        variableAndTypeTmp = leftVariableAndType[0].trim();
                    }

                    if(!variableAndTypeTmp.contains(UnitTestClassConstants.SEPARATOR_SPACE_ONE)){
                        continue;
                    }
                    String varType = variableAndTypeTmp.substring(0, variableAndTypeTmp.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                    String varName = variableAndTypeTmp.substring(variableAndTypeTmp.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                    if(ObjectUtils.nullSafeEquals(varName, variableName)){
                        return varType;
                    }
                }else{
                    //eg: List<UserDTO> userDTOS
                    String varType = minExpression.substring(0, minExpression.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                    String varName = minExpression.substring(minExpression.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                    if(ObjectUtils.nullSafeEquals(varName, variableName)){
                        return varType;
                    }
                }
                continue;
            }
            if(node instanceof VariableDeclarationExpr){
                String variableDeclarationExpr = node.toString().trim();
                String varType = variableDeclarationExpr.substring(0, variableDeclarationExpr.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                String varName = variableDeclarationExpr.substring(variableDeclarationExpr.lastIndexOf(UnitTestClassConstants.SEPARATOR_SPACE_ONE)).trim();
                if(ObjectUtils.nullSafeEquals(varName, variableName)){
                    return varType;
                }
                continue;
            }
            if(CollectionUtils.isEmpty(node.getChildNodes())){
                logger.warn("node:{} ,ChildNodes为空",node);
                continue;
            }
            String typeByVariableName = getTypeByVariableName(variableName, node.getChildNodes());
            if(ObjectUtils.isEmpty(typeByVariableName)){
                continue;
            }
            return typeByVariableName;
        }
        return null;
    }

    /**
     * 获取表达式中的参数
     * eg: addressService.getAddressByUserCode(userCode,userName) -> userCode userName
     *     addressService.getAddressByUserCode(userDTO.getUserCode()) -> userDTO
     * @param expression
     * @return
     */
    private static List<String> getExpressionParameterList(String expression, MethodModel methodModel){
        if(ObjectUtils.isEmpty(expression)){
            return Lists.newArrayList();
        }
        String parameterMore = getExpressionParam(expression.trim());
        if(ObjectUtils.isEmpty(parameterMore)){
            return Lists.newArrayList();
        }
        List<String> paramList = Lists.newArrayList();
        String[] params = parameterMore.split(UnitTestClassConstants.SEPARATOR_COMMA);
        for(String param : params){
            String tmpParam = param.trim();
            /**
             * 特殊参数-函数参数-获取函数参数
             */
            if(tmpParam.contains(".") && tmpParam.contains("(") && tmpParam.contains(")")){
                List<String> expressionParameterList = getExpressionParameterList(tmpParam, methodModel);
                if(!CollectionUtils.isEmpty(expressionParameterList)){
                    paramList.addAll(expressionParameterList);
                    continue;
                }
            }
            if(LStringUtils.isEmptyAndNull(tmpParam)){
                continue;
            }
            if(tmpParam.contains(".")){
                tmpParam = tmpParam.substring(0, tmpParam.indexOf(".")).trim();
            }

            if(isFieldClassVar(tmpParam)){
                continue;
            }

            if(isMethodParameterOrVariable(tmpParam, methodModel)){
                if(!CollectionUtils.contains(paramList.listIterator(), tmpParam)){
                    paramList.add(tmpParam);
                }
            }else{
                /**
                 * 不考虑常量类
                 * 按import是否导入判断
                 */
                if(!sourceClassModel.existImport(tmpParam)){
                    if(!CollectionUtils.contains(paramList.listIterator(), tmpParam)){
                        paramList.add(tmpParam);
                    }
                }
            }
        }
        return paramList;
    }

    /**
     * 判断该变量是否 类变量
     * @param var
     * @return
     */
    private static boolean isFieldClassVar(String var){
        List<FieldModel> fieldModels = sourceClassModel.getFieldModels();
        for (FieldModel fieldModel : fieldModels) {
            if(StringUtils.equals(fieldModel.getName(), var)){
                return true;
            }
        }
        Set<Map.Entry<String, FieldModel>> fieldEntries = sourceClassModel.getInstantiateFieldModels().entrySet();
        for (Map.Entry<String, FieldModel> fieldEntry : fieldEntries) {
            if(StringUtils.equals(fieldEntry.getKey(), var)){
                FieldModel fieldModel = fieldEntry.getValue();
                addQueue(fieldModel.getType() + " " + fieldModel.getName() + " = " + fieldModel.getValue());
                return true;
            }
        }
        return false;
    }

    /**
     * 截取方法参数
     * @param expressionStr
     * @return
     */
    private static String getExpressionParam(String expressionStr){
        String reverse = new StringBuffer(expressionStr).reverse().toString();
        Stack<Character> stack = new Stack<Character>();
        int offset = 0;
        char tmp = 0;
        char[] chars = reverse.toCharArray();
        for(int i = 0; i < chars.length; i++){
            if(stack.empty() && chars[i] == ')'){
                stack.push(chars[i]);
                tmp = chars[i];
                continue;
            }
            if(chars[i] == tmp ){
                stack.push(chars[i]);
            }else if(chars[i] == '('){
                Character pop = stack.pop();
                if(stack.empty() && pop == ')'){
                    offset = i;
                    break;
                }
            }
        }
        return new StringBuffer(reverse.substring(1, offset)).reverse().toString();
    }

    /**
     * 构建方法参数模型 和 构建单测方法体-入参
     * @param parameters
     * @return
     */
    private static Map<String, ParameterModel> buildMethodParameter(NodeList<Parameter> parameters) {
        Map<String, ParameterModel> tmpParameters = Maps.newLinkedHashMap();
        for (Parameter parameter : parameters){
            ParameterModel parameterModel= new ParameterModel();
            parameterModel.setName(parameter.getNameAsString());
            parameterModel.setType(parameter.getType().asString());
            parameterModel.setFullQualifiedName(getFullQualifiedName(parameter.getType().asString(), sourceClassModel));
            tmpParameters.put(parameterModel.getName(), parameterModel);
            /*构建-单测方法体-入参*/
            String declareExpression = autoCreateObject(parameterModel.getType(), parameterModel.getName());
            addQueue(declareExpression);
        }
        return tmpParameters;
    }

    /**
     * 自动实例对象并赋值
     * @param type
     * @param name
     * @return
     */
    private static String autoCreateObject(String type, String name){
        type = type.trim();
        name = name.trim();
        if(type.contains("<") && type.contains(">")){
            String collectionType = type.substring(0, type.indexOf("<"));
            String elementType = type.substring(type.indexOf("<") + 1, type.lastIndexOf(">"));
            String[] splitTypes = elementType.split(",");
            String args = "";
            for (String splitType : splitTypes){
                args += splitType+".class,";
            }
            return type + " " + name + " = podamFactory.manufacturePojo("+ collectionType + ".class," + args.substring(0, args.length() - 1) +")";
        }
        return  type + " " + name + " = podamFactory.manufacturePojo(" + type + ".class)";
    }

    /**
     * 获取集合元素类型
     * @param collectionType
     * @return
     */
    private static String getCollectionElementType(String collectionType){
        collectionType = collectionType.trim();
        if(collectionType.contains("<") && collectionType.contains(">")){
            String elementType = collectionType.substring(collectionType.indexOf("<") + 1, collectionType.indexOf(">"));
            return elementType;
        }
        return collectionType;
    }

    /**
     * 预期异常
     * thrown.expect(Exception.class);
     * @param type
     * @return
     */
    private static String thrownExpect(String type){
        return "thrown.expect(" +
                type +
                ".class)";
    }

    /**
     * 根据字符类名获取-类全限定名
     * @param type
     * @param sourceClassModel
     * @return
     */
    private static String getFullQualifiedName(String type, SourceClassModel sourceClassModel){
        if("java.lang.String".contains(type)){
            return "java.lang.String";
        }
        if("java.lang.Integer".contains(type) || "int".equals(type)){
            return "java.lang.Integer";
        }
        if("java.lang.Boolean".contains(type) || "boolean".equals(type)){
            return "java.lang.Boolean";
        }
        Map<String, String> importGenerals = sourceClassModel.getImportGenerals();
        return importGenerals.get(type);
    }

    /**
     * 判断该表达式是否需要Mock
     * @param expressionStr
     * @return 0:不需要Mock; 1: 注入字段Mock; 2：类方法Mock
     */
    private static int isMock(String expressionStr, MethodModel methodModel){
        String expression = expressionStr;
        if(expression.contains("=")){
            String[] split = expression.split(UnitTestClassConstants.SEPARATOR_EQUALS);
            expression = split[1].trim();
        }else if(expression.contains(".")){
            //eg: userMapper.saveUser(userDTO);
            String[] split = expression.split(UnitTestClassConstants.SEPARATOR_DOT);
            expression = split[0].trim() + ".";
        }
        //Mock字段
        List<FieldModel> fieldModels = sourceClassModel.getFieldModels();
        for(FieldModel fieldModel : fieldModels){
            //eg: AddressDTO addressByUserCode = addressService.getAddressByUserCode(userCode);
            if(expression.contains(fieldModel.getName() + ".")){
                logger.info("需要Mock表达式：{}", expressionStr);
                addVerfy(expressionStr, methodModel);
                return 1;
            }
        }
        /*
        TODO 后续考虑Mock类方法
         */
//        if(expression.contains("(") && expression.contains(")")){
//            if(expression.contains("this.")){
//                expression = expression.replaceAll("this.", "");
//            }
//            List<String> methodNames = sourceClassModel.getMethodNames();
//            String var = expression.substring(0, expression.indexOf("(")).trim();
//            if(!CollectionUtils.isEmpty(methodNames) && methodNames.contains(var)){
//                return 2;
//            }
//        }
        return 0;
    }

    /**
     *  断言
     * @param expressionAssert
     */
    private static void addAssert(String expressionAssert){
        asserts.add(getAssertNotNull(expressionAssert));
    }

    /**
     * @param assertModel
     * @return
     */
    private static String getAssertNotNull(String assertModel){
        return "assertNotNull(" + assertModel + ")";
    }

    /**
     *  验证
     * @param verfyModel
     */
    private static void addVerfy(String verfyModel, MethodModel methodModel){
        MockExpressionModel mockExpressionModel = toMockExpressionModel(verfyModel, methodModel);
        if(mockExpressionModel.getExpression().contains("new ")){
            return;
        }
        verifies.add(getVerfyTimes(mockExpressionModel));
    }

    /**
     * @param mockExpressionModel
     * @return
     */
    private static String getVerfyTimes(MockExpressionModel mockExpressionModel){
        String expression = mockExpressionModel.getExpression();
        String instance = expression.substring(0, expression.indexOf("."));
        String method = expression.substring(expression.indexOf(".") + 1, expression.length());
        return "verify(" + instance.trim() + ", times(1))." + method;
    }

    /**
     * 判断是否为本类方法，并Mock
     * getUser(userCode)
     * @param expressionStmt
     * @param methodModel
     */
    private static boolean isMockInnerMethod(String expressionStmt, MethodModel methodModel){
        try{
            Class<?> clazz = Class.forName(sourceClassModel.getPackageName() + "." + sourceClassModel.getClassName());
            String methodType = LReflectionUtils.getMethodTypeByName(clazz, methodModel.getMethodName(), methodModel);
            return ObjectUtils.isEmpty(methodType) ? false : true;
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        return false;
    }

}
